package network.rpc.server;


import network.rpc.protocol.RpcRequest;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 简单地基于TCP通信和BIO实现服务中心
 */
public class ServiceCenterImpl implements ServiceCenter {
    // 这里简单的使用BIO模型
    // 所以需要使用一个线程池来提供服务
    private ExecutorService threadPool
            = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    // 服务的注册中心
    // 你要知道有什么服务，你才能够提供服务
    private final HashMap<String, Class<?>> registry = new HashMap<>();

    // 网络通信最终是进程的通信
    // 需要精细到端口，才能定位到一个进程的粒度
    private int port;

    // 标识当前的服务中心状态
    private boolean isRunning = false;

    public ServiceCenterImpl(int port) {
        this.port = port;
    }

    // 最终分配给线程的是可运行的任务
    // 这里就将线程到底要干什么封装
    private class ServiceTask implements Runnable {
        // 线程的任务就是要服务远程的客户
        // 网络通信就必须获取ip地址和端口，socket就是这两者的封装
        Socket clientSocket;

        public ServiceTask(Socket client) {
            this.clientSocket = client;
        }

        @Override
        public void run() {

            // TCP是字节流的传输，所以对象必然是序列化成字节码之后发过来
            // 所以需要获取对象的流输入输出
            try (ObjectInputStream ins = new ObjectInputStream(clientSocket.getInputStream());
                 ObjectOutputStream outs = new ObjectOutputStream(clientSocket.getOutputStream())) {
                System.out.println("# Service center connected to client");

                RpcRequest req = (RpcRequest) ins.readObject();
                String serviceName = req.getServiceName();
                String methodName = req.getMethodName();
                Class<?>[] paraTypes = req.getParaTypes();
                Object[] params = req.getParams();

                Class<?> serviceClass = registry.get(serviceName);
                if (serviceClass == null) {
                    throw new ClassNotFoundException(serviceName + " not found in service center");
                }

                // 调用服务，获取结果
                Method method = serviceClass.getMethod(methodName, paraTypes);
                Object result = method.invoke(serviceClass.newInstance(), params);

                // 将结果返回给客户端
                outs.writeObject(result);

                System.out.println("# Service center do service and return result");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (clientSocket != null) {
                        clientSocket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
    }


    @Override
    public void start() throws IOException {
        // 服务中心拥有了socket就打开了网络的通信的门
        try (ServerSocket serverSocket = new ServerSocket()) {
            serverSocket.bind(new InetSocketAddress(port)); // socket = (ip地址，端口)
            System.out.println("Service center start :)");
            while (true) {
                threadPool.execute(new ServiceTask(serverSocket.accept()));
            }
        }
    }

    @Override
    public void stop() {
        isRunning = false;
        threadPool.shutdown();
    }

    @Override
    public void register(Class<?> serviceInterface, Class<?> impl) {
        registry.put(serviceInterface.getName(), impl);
    }

    @Override
    public int getPort() {
        return this.port;
    }
}
